/**
 *
 */
package de.cbf.cam.dsa.rcp.model.entities;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

import de.cbf.util.uuidheader.entities.EntityWeighted;
import de.cbf.util.uuidheader.entities.UUIDReference;
import de.cbf.util.uuidheader.entities.NamedEntity;
import de.cbf.util.uuidheader.entities.UUIDHeader;
import de.cbf.util.uuidheader.exceptions.EntityMergeException;
import de.cbf.util.uuidheader.util.EntityWeightedList;

/**
 * @author salter
 *
 */
@XmlRootElement(name = "structure")
public class StructureData extends NamedEntity {
	public static final String 	P_RACE_CULTURES			= "race_cultures";
	public static final String 	P_CULTURE_PROFESSIONS 	= "culture_professions";

	public static final int 	PI_RACE_CULTURES 		= 14;
	public static final int 	PI_CULTURE_PROFESSIONS 	= 15;



	@XmlElementWrapper(name="race_cultures")
	protected Map<UUIDReference,EntityWeightedList>	raceCultures		;
	@XmlElementWrapper(name="culture_professions")
	protected Map<UUIDReference,EntityWeightedList> 	cultureProfessions	;

	/**
	 *
	 */
	public StructureData() {
		raceCultures = new HashMap<UUIDReference, EntityWeightedList>();
		cultureProfessions = new HashMap<UUIDReference, EntityWeightedList>();
	}

	/* (non-Javadoc)
	 * @see de.cbf.util.uuidheader.entities.UUIDHeader#doMergeDataFrom(de.cbf.util.uuidheader.entities.UUIDHeader, boolean)
	 */
	@Override
	protected void doMergeDataFrom(UUIDHeader entity, boolean i_am_older)
			throws EntityMergeException {
		super.doMergeDataFrom(entity, i_am_older);
		if (entity instanceof StructureData) {
			StructureData correctEntity = (StructureData) entity;
			if(i_am_older){
				this.raceCultures.clear();
				this.raceCultures.putAll(correctEntity.raceCultures);
				this.cultureProfessions.clear();
				this.cultureProfessions.putAll(correctEntity.cultureProfessions);
			}
		}else{
			String mergeEntityClass = entity == null? "null":entity.getClass().getName();
			String errorMsg ="EntityToMerge "+mergeEntityClass+" does not fit class:"+this.getClass().getName();
			throw new EntityMergeException(errorMsg);
		}

	}

	public Set<UUIDReference> getRaces(){
		Set<UUIDReference> result = new HashSet<UUIDReference>(raceCultures.keySet());
		return result;
	}

	public Set<UUIDReference> getCultures(){
		Set<UUIDReference> result = new HashSet<UUIDReference>(cultureProfessions.keySet());
		return result;
	}

	public EntityWeightedList culturesForRace(UUIDReference raceReference){
		return raceCultures.get(raceReference);
	}

	public EntityWeightedList racesForCulture(UUIDReference cultureReference){
		EntityWeightedList result = new EntityWeightedList();
		Set<Entry<UUIDReference, EntityWeightedList>> entries = raceCultures.entrySet();
		for (Entry<UUIDReference, EntityWeightedList> entry : entries) {
			for (EntityWeighted cultureWeighted : entry.getValue()) {
				if(cultureWeighted.getEntity().equals(cultureReference)){
					EntityWeighted raceWeighted = new EntityWeighted(entry.getKey(),cultureWeighted.getProbabilitySpan());
					result.add(raceWeighted);
				}
			}
		}
		return result;
	}
	public EntityWeightedList professionsForCulture(UUIDReference cultureReference){
		return cultureProfessions.get(cultureReference);
	}

	public EntityWeightedList culturesForProfession(UUIDReference professionReference){
		EntityWeightedList result = new EntityWeightedList();
		Set<Entry<UUIDReference, EntityWeightedList>> entries = cultureProfessions.entrySet();
		for (Entry<UUIDReference, EntityWeightedList> entry : entries) {
			for (EntityWeighted professionWeighted : entry.getValue()) {
				if(professionWeighted.getEntity().equals(professionReference)){
					EntityWeighted cultureWeighted = new EntityWeighted(entry.getKey(),professionWeighted.getProbabilitySpan());
					result.add(cultureWeighted);
				}
			}
		}
		return result;
	}

	/**
	 * only adds cultures that are not yet existent, merge weights otherwise
	 * @param race
	 * @param cultures
	 */
	public void addCulturesToRace(UUIDReference race,EntityWeightedList cultures){
		EntityWeightedList culturesInRace = raceCultures.get(race);
		if(culturesInRace == null){
			culturesInRace = new EntityWeightedList();
		}
		boolean changed = false;
		//go through list and add cultures if they are  not present
		int weightBehavior = culturesInRace.getDefaultWeightBehavior();
		culturesInRace.setDefaultWeightBehavior(EntityWeightedList.BEHAVIOR_WEIGHTS_ADD);
		changed = culturesInRace.addAll(cultures);
		culturesInRace.setDefaultWeightBehavior(weightBehavior);
		if(changed){
			raceCultures.put(race, culturesInRace);
			propertyChangeSupport.fireIndexedPropertyChange(P_RACE_CULTURES, PI_RACE_CULTURES, null,
					this.raceCultures);
		}
	}


	public void addProfessionsToCulture(UUIDReference culture,EntityWeightedList professions){
		EntityWeightedList professionsInCulture = cultureProfessions.get(culture);
		if(professionsInCulture == null){
			professionsInCulture = new EntityWeightedList();
		}
		boolean changed = false;
		//go through list and add cultures if they are  not present
		int weightBehavior = professionsInCulture.getDefaultWeightBehavior();
		professionsInCulture.setDefaultWeightBehavior(EntityWeightedList.BEHAVIOR_WEIGHTS_ADD);
		changed = professionsInCulture.addAll(professions);
		professionsInCulture.setDefaultWeightBehavior(weightBehavior);
		if(changed){
			cultureProfessions.put(culture, professionsInCulture);
			propertyChangeSupport.fireIndexedPropertyChange(P_CULTURE_PROFESSIONS, PI_CULTURE_PROFESSIONS, null,
					this.cultureProfessions);
		}
	}

	public boolean mergeInRaceCultures(Map<UUIDReference,EntityWeightedList> raceCulturesNew){
		boolean changed = false;
		for (UUIDReference raceNew : raceCulturesNew.keySet()) {
			//see if there are already cultures registered to that race
			EntityWeightedList culturesAlreadyThere = this.raceCultures.get(raceNew);
			if(culturesAlreadyThere != null){
				//get new cultures and add them, overwriting previous weights if culture already exists
				EntityWeightedList culturesNew = raceCulturesNew.get(raceNew);
				int merge_result = culturesAlreadyThere.mergeList(culturesNew, EntityWeightedList.BEHAVIOUR_LIST_ADD_NO_DOUBLE, EntityWeightedList.BEHAVIOR_WEIGHTS_OVERWRITE);
				culturesAlreadyThere.cleanListOfZeros();
				if(merge_result == EntityWeightedList.RETURN_MERGED){
					changed = true;
				}
			}else{
				//no cultures found for this race -> add them
				this.raceCultures.put(raceNew, raceCulturesNew.get(raceNew));
				changed = true;
			}
		}
		if(changed){
			propertyChangeSupport.fireIndexedPropertyChange(P_RACE_CULTURES, PI_RACE_CULTURES, null,
					this.raceCultures);
		}
		return changed;
	}

	public boolean mergeInCultureProfessions(Map<UUIDReference,EntityWeightedList> cultureProfessionsNew){
		boolean changed = false;
		for (UUIDReference cultureNew : cultureProfessionsNew.keySet()) {
			//see if there are already cultures registered to that race
			EntityWeightedList professionsAlreadyThere = this.cultureProfessions.get(cultureNew);
			if(professionsAlreadyThere != null){
				//get new cultures and add them, overwriting previous weights if culture already exists
				EntityWeightedList professionsNew = cultureProfessionsNew.get(cultureNew);
				int merge_result = professionsAlreadyThere.mergeList(professionsNew, EntityWeightedList.BEHAVIOUR_LIST_ADD_NO_DOUBLE, EntityWeightedList.BEHAVIOR_WEIGHTS_OVERWRITE);
				professionsAlreadyThere.cleanListOfZeros();
				if(merge_result == EntityWeightedList.RETURN_MERGED){
					changed = true;
				}
			}else{
				//no entries for this culture so add the ones provided
				this.cultureProfessions.put(cultureNew, cultureProfessionsNew.get(cultureNew));
				changed = true;
			}
		}
		if(changed){
			propertyChangeSupport.fireIndexedPropertyChange(P_CULTURE_PROFESSIONS, PI_CULTURE_PROFESSIONS, null,
					this.cultureProfessions);
		}
		return changed;
	}

	public Map<UUIDReference, EntityWeightedList> getRaceCultures() {
		return raceCultures;
	}

	public Map<UUIDReference, EntityWeightedList> getCultureProfessions() {
		return cultureProfessions;
	}
}
